home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 / Aminet - June 1993 [Walnut Creek].iso / usenet / sources / volume90 / unix / uucp106d / part07 < prev    next >
Encoding:
Internet Message Format  |  1990-06-28  |  44.5 KB

  1. Path: xanth!cs.odu.edu!Amiga-Request
  2. From: Amiga-Request@cs.odu.edu (Amiga Sources/Binaries Moderator)
  3. Newsgroups: comp.sources.amiga
  4. Subject: v90i185: UUCP 1.06D - UNIX compatible uucp/news/mail system, Part07/12
  5. Message-ID: <12976@xanth.cs.odu.edu>
  6. Date: 28 Jun 90 12:23:02 GMT
  7. Sender: news@cs.odu.edu
  8. Reply-To: Matt Dillon <@uunet.uu.net:overload!dillon>
  9. Lines: 1811
  10. Approved: tadguy@cs.odu.edu (Tad Guy)
  11. X-Mail-Submissions-To: Amiga@cs.odu.edu
  12. X-Post-Discussions-To: comp.sys.amiga
  13.  
  14. Submitted-by: Matt Dillon <@uunet.uu.net:overload!dillon>
  15. Posting-number: Volume 90, Issue 185
  16. Archive-name: unix/uucp-1.06d/part07
  17.  
  18. #!/bin/sh
  19. # This is a shell archive.  Remove anything before this line, then unpack
  20. # it by saving it into a file and typing "sh file".  To overwrite existing
  21. # files, type "sh file -c".  You can also feed this as standard input via
  22. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  23. # will see the following message at the end:
  24. #        "End of archive 7 (of 12)."
  25. # Contents:  uucp2/src/dmail/execom.c uucp2/src/dmail/load_mail.c
  26. #   uucp2/src/uuser/uuser.c
  27. # Wrapped by tadguy@xanth on Thu Jun 28 08:21:29 1990
  28. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  29. if test -f 'uucp2/src/dmail/execom.c' -a "${1}" != "-c" ; then 
  30.   echo shar: Will not clobber existing file \"'uucp2/src/dmail/execom.c'\"
  31. else
  32. echo shar: Extracting \"'uucp2/src/dmail/execom.c'\" \(12837 characters\)
  33. sed "s/^X//" >'uucp2/src/dmail/execom.c' <<'END_OF_FILE'
  34. X
  35. X/*
  36. X *  EXECOM.C
  37. X *
  38. X *  $Header: Beta:src/uucp/src/dmail/RCS/execom.c,v 1.1 90/02/02 12:03:41 dillon Exp Locker: dillon $
  39. X *
  40. X *  (C) Copyright 1985-1990 by Matthew Dillon,  All Rights Reserved.
  41. X *
  42. X *  Routines to parse and execute command lines.
  43. X *
  44. X *  Global Routines:    DO_COMMAND()
  45. X *            EXEC_COMMAND()
  46. X *            FIX()
  47. X *
  48. X *  Static Routines:    E_COMMAND()
  49. X *            BREAKOUT()
  50. X *            FIND_COMMAND()
  51. X */
  52. X
  53. X
  54. X#include <pwd.h>
  55. X#include <stdio.h>
  56. X#include <string.h>
  57. X#include "dmail.h"
  58. X#include "execom.h"
  59. X
  60. X#define F_EXACT     0
  61. X#define F_ABBR        1
  62. X#define SCRBUF        1024
  63. X
  64. Xextern char *breakout();
  65. X
  66. Xextern int do_quit(), do_exit(), do_help(), do_list(), do_setlist();
  67. Xextern int do_select(), do_type(), do_header(), do_next(), do_mark();
  68. Xextern int do_unmark(), do_reply(), do_delnext(), do_rlist();
  69. Xextern int do_write(), do_shell(), do_set_var(), do_unset_var();
  70. Xextern int do_number(), do_cd(), do_source(), do_defer(), do_echo();
  71. Xextern int do_go(), do_break();
  72. X
  73. Xextern int do_if(), do_else(), do_endif();
  74. Xextern int do_ver(), do_delprev();
  75. X
  76. Xstruct COMMAND Command[] = {
  77. X    do_number   , 0,    0,            "",
  78. X    do_mark     , 0,    ST_DELETED,        "delete",
  79. X    do_unmark   , 0,    ST_DELETED,        "undelete",
  80. X    do_header   , 0,    0,            "header",
  81. X    do_type     , 0,    0,            "type",
  82. X    do_echo     , 0,    0,            "echo",
  83. X    do_go        , 0,    0,            "go",
  84. X    do_reply    , 0,    R_REPLY,        "reply",
  85. X    do_reply    , 0,    R_INCLUDE,        "Reply",
  86. X    do_reply    , 0,    R_MAIL,         "mail",
  87. X    do_reply    , 0,    R_FORWARD,        "forward",
  88. X    do_select   , 0,    0,            "select",
  89. X    do_select   , 0,    1,            "reselect",
  90. X    do_defer    , 0,    1,            "defer",
  91. X    do_list     , 0,    0,            "list",
  92. X    do_rlist    , 0,    0,            "rlist",
  93. X    do_next     , 0,    1,            "next",
  94. X    do_next     , 0,    -1,            "back",
  95. X    do_next     , 0,    2,            "_next",
  96. X    do_next     , 0,    -2,            "_back",
  97. X    do_delnext  , 0,    0,            "dt",
  98. X    do_delprev  , 0,    0,            "db",
  99. X    do_set_var  , 0,    0,            "set",
  100. X    do_unset_var, 0,    0,            "unset",
  101. X    do_set_var  , 0,    1,            "alias",
  102. X    do_unset_var, 0,    1,            "unalias",
  103. X    do_set_var  , C_NO,    2,            "malias",
  104. X    do_unset_var, C_NO,    2,            "munalias",
  105. X    do_setlist  , 0,    0,            "setlist",
  106. X    do_cd        , 0,    0,            "cd",
  107. X    do_source   , 0,    0,            "source",
  108. X    do_unmark   , 0,    ST_READ | ST_STORED,"preserve",
  109. X    do_mark     , 0,    ST_READ,        "mark",
  110. X    do_mark     , 0,    ST_TAG,         "tag",
  111. X    do_unmark   , 0,    ST_TAG,         "untag",
  112. X    do_unmark   , 0,    ST_STORED,        "unwrite",
  113. X    do_write    , 0,    0,            "write",
  114. X    do_shell    , 0,    0,            "!",
  115. X    do_exit     , 0,    0,            "x",
  116. X    do_quit     , 0,    0,            "quit",
  117. X    do_exit     , 0,    1,            "xswitch",
  118. X    do_quit     , 0,    1,            "qswitch",
  119. X    do_help     , 0,    0,            "help",
  120. X    do_help     , 0,    0,            "?",
  121. X    do_break    , 0,    0,            "nobreak",
  122. X    do_break    , 0,    1,            "breakok",
  123. X    do_if        , C_COND,    0,            "if",
  124. X    do_else     , C_COND,    0,            "else",
  125. X    do_endif    , C_COND,    0,            "endif",
  126. X    do_ver        , 0,    0,            "version",
  127. X    NULL        , 0,    0,            NULL };
  128. X
  129. Xchar *Desc[] = {
  130. X    "",
  131. X    "<list>                   mark messages for deletion",
  132. X    "<list>                   UNDELETE & UNMARK messages",
  133. X    "[msg]                    Display header of a message",
  134. X    "[msg]                    type a message",
  135. X    "args....                 Echo to the screen",
  136. X    "#                        Go to a message, don't print out",
  137. X    "                         reply to mail",
  138. X    "                         reply to mail, include recv'd text",
  139. X    "user user ...            send mail to users",
  140. X    "user user ...            forward mail to users",
  141. X    "Field [!]match [match][ , Field match.]  SELECT from entire message list",
  142. X    "Field [!]match [match][ , Field match.]  SELECT from current message list",
  143. X    "                         De-select any read messages",
  144. X    "<list>                   list mail as specified by SETLIST",
  145. X    "[+/-][N]                 list relative to current position",
  146. X    "[msg]                    type/header next or message #",
  147. X    "[msg]                    type/header previous or message #",
  148. X    "[msg]                    go to next or message #",
  149. X    "[msg]                    go to previous or message #",
  150. X    "                         delete current, type next",
  151. X    "                         delete current, type prev",
  152. X    "[var [string]]           set a variable",
  153. X    "var var var ...          unset a variable",
  154. X    "[var [string]]           set an alias",
  155. X    "var var var ...          unset an alias",
  156. X    "[var [string]]           set a mail alias",
  157. X    "var var var ...          unset a mail alias",
  158. X    "[-s] [cols] Field [cols] Field...    SET LIST format for LIST",
  159. X    "path                     CD to a directory",
  160. X    "file                     Source a file",
  161. X    "<list>                   UNREAD & UNMARK messages",
  162. X    "<list>                   mark messages as 'read'",
  163. X    "<list>                   tag messages for whatever",
  164. X    "<list>                   untag messages",
  165. X    "<list>                   unwrite messages",
  166. X    "file <list>              append messages to a file, delete on quit",
  167. X    "[command]                execute a shell [command]",
  168. X    "                         EXIT, do not save changes",
  169. X    "                         QUIT, update files",
  170. X    "from to                  Exit and switch to a new from/to file",
  171. X    "from to                  Quit and switch to a new from/to file",
  172. X    "[topic]                  help on a topic",
  173. X    "[topic]                  alternate form of HELP",
  174. X    "                         Disable INTR (stackable)",
  175. X    "                         Enable  INTR (stackable)",
  176. X    "[!]variable              conditionals (stackable)",
  177. X    "",
  178. X    "",
  179. X    "                         Print the version number",
  180. X    NULL };
  181. X
  182. X
  183. Xdo_command()
  184. X{
  185. X    static char comline[1024];
  186. X
  187. X    if (Current >= 0 && Current < Entries)
  188. X    printf("%3d:", Entry[Current].no);
  189. X    else
  190. X    printf("nul:");
  191. X    fflush (stdout);
  192. X    if (gets (comline) == NULL)
  193. X    done (1);
  194. X    exec_command(comline);
  195. X    return (1);
  196. X}
  197. X
  198. X
  199. X
  200. X/*
  201. X * EXEC_COMMAND()
  202. X *
  203. X *
  204. X */
  205. X
  206. X
  207. Xstruct MLIST {
  208. X    struct MLIST *next;
  209. X};
  210. X
  211. Xstatic struct MLIST *Mlist;
  212. X
  213. Xchar *
  214. Xmpush(amount)
  215. Xint amount;
  216. X{
  217. X    struct MLIST *ml;
  218. X
  219. X    push_break();
  220. X    ml = (struct MLIST *)malloc (amount + sizeof(*Mlist));
  221. X    ml->next = Mlist;
  222. X    Mlist = ml;
  223. X    pop_break();
  224. X    return ((char *)Mlist + sizeof(*Mlist));
  225. X}
  226. X
  227. X
  228. Xchar *
  229. Xmpop()
  230. X{
  231. X    char *old = NULL;
  232. X
  233. X    push_break();
  234. X    if (Mlist == NULL) {
  235. X    puts ("MLIST INTERNAL ERROR");
  236. X    } else {
  237. X    old = (char *)Mlist + sizeof(*Mlist);
  238. X    xfree (Mlist);
  239. X    Mlist = Mlist->next;
  240. X    }
  241. X    pop_break();
  242. X    return (old);
  243. X}
  244. X
  245. Xvoid
  246. Xmrm()
  247. X{
  248. X    push_break();
  249. X    while (Mlist) {
  250. X    xfree (Mlist);
  251. X    Mlist = Mlist->next;
  252. X    }
  253. X    pop_break();
  254. X}
  255. X
  256. X
  257. Xexec_command(base)
  258. Xchar *base;
  259. X{
  260. X    char *str;
  261. X    int i;
  262. X
  263. X    if (push_base()) {
  264. X    push_break();
  265. X    pop_base();
  266. X    mrm();
  267. X    pop_break();
  268. X    return (-1);
  269. X    }
  270. X    strcpy (str = mpush(strlen(base) + 1), base);
  271. X    i = e_command(str);
  272. X    if (mpop() != str)
  273. X    puts ("POP ERROR");
  274. X    pop_base();
  275. X    return (i);
  276. X}
  277. X
  278. X
  279. Xstatic
  280. Xe_command(base)
  281. Xchar *base;
  282. X{
  283. X    char *com, *start, *avline, *alias;
  284. X    int flag = 0;
  285. X    int i, pcount, len, ccno;
  286. X
  287. Xloop:
  288. X    com = breakout (&base, &flag);
  289. X    if (*com == '\0') {
  290. X    if (flag > 1)
  291. X        return (1);
  292. X    goto loop;
  293. X    }
  294. X    if ((ccno = find_command(com, F_EXACT)) < 0) {
  295. X    if (*com == '$')
  296. X        alias = get_var (LEVEL_SET, com + 1);
  297. X    else
  298. X        alias = get_var (LEVEL_ALIAS, com);
  299. X    if (alias == NULL) {
  300. X        if ((ccno = find_command (com, F_ABBR)) < 0) {
  301. X        if (!XDisable)
  302. X            printf ("%s Command Not found\n", com);
  303. X        return (XDisable ? 1 : -1);
  304. X        } else {
  305. X        goto good_command;
  306. X        }
  307. X    }
  308. X
  309. X    /* At this point, base points to arguments */
  310. X
  311. X    start = (flag == 0) ? base : "";
  312. X    while (flag == 0) {             /* find ';' or end of string        */
  313. X        flag = -1;            /* disable breakout's "" terminator */
  314. X        breakout (&base, &flag);
  315. X    }
  316. X
  317. X    /*
  318. X     * At this point, start points to all arguments, base set up for next
  319. X     * string
  320. X     */
  321. X
  322. X    if (*alias == '%') {
  323. X        int xx = 0;
  324. X        char *select;
  325. X
  326. X        alias = strcpy (mpush (strlen(alias) + 1), alias);
  327. X        select = breakout (&alias, &xx);
  328. X        set_var (LEVEL_SET, select + 1, start);
  329. X        i = e_command (alias);
  330. X        unset_var (LEVEL_SET, select + 1);
  331. X        mpop();
  332. X    } else {
  333. X        com = mpush (strlen(alias) + strlen(start) + 2);
  334. X        strcpy (com, alias);
  335. X        strcat (com, (flag == 1) ? ";" : " ");
  336. X        strcat (com, start);
  337. X        i = e_command (com);
  338. X        if (mpop() != com)
  339. X        puts ("ME BAE ERROR");
  340. X    }
  341. X    if (i < 0)
  342. X        return (-1);
  343. X    if (flag > 1)
  344. X        return (1);
  345. X    goto loop;
  346. X    }
  347. Xgood_command:
  348. X    if (XDisable && (Command[ccno].stat & C_COND) == 0) {
  349. X    while (flag < 1)
  350. X        breakout (&base, &flag);
  351. X    if (flag > 1)
  352. X        return (1);
  353. X    goto loop;
  354. X    }
  355. X    if (Command[ccno].stat & C_NO  &&  XDebug == 0) {
  356. X    printf ("%s  Is currently being developed\n", Command[ccno].name);
  357. X    return (-1);
  358. X    }
  359. X    if (XDebug)
  360. X    printf ("Good command, Raw: %s\n", com);
  361. X    i = pcount = 0;
  362. X    av[i] = mpush (strlen(com) + 1);
  363. X    ++pcount;
  364. X    strcpy (av[i++], com);
  365. X    while (flag < 1) {
  366. X    com = breakout (&base, &flag);
  367. X    if (XDebug)
  368. X        printf ("BREAKOUT %d %s\n", strlen(com), com);
  369. X    if (*com == '\0')
  370. X        continue;
  371. X    switch (*com) {
  372. X    case '~':
  373. X        if (com[1] == '/'  ||  com[1] == '\0') {
  374. X        av[i] = mpush (strlen(home_dir) + strlen(com + 1) + 1);
  375. X        ++pcount;
  376. X        strcpy (av[i], home_dir);
  377. X        strcat (av[i], com + 1);
  378. X        } else {
  379. X        struct passwd *passwd;
  380. X        char *user = com;
  381. X
  382. X        while (*com) {
  383. X            if (*com == '/') {
  384. X            *com = '\0';
  385. X            ++com;
  386. X            break;
  387. X            }
  388. X            ++com;
  389. X        }
  390. X        if ((passwd = getpwnam(user)) == NULL) {
  391. X            printf ("USER %s Not found\n", user);
  392. X            while (pcount--)
  393. X            mpop();
  394. X            return (-1);
  395. X        }
  396. X        av[i] = mpush (strlen(passwd->pw_dir) + strlen(com) + 2);
  397. X        ++pcount;
  398. X        strcpy (av[i], passwd->pw_dir);
  399. X        if (*com) {
  400. X            strcat (av[i], "/");
  401. X            strcat (av[i], com);
  402. X        }
  403. X        }
  404. X        break;
  405. X    case '\"':
  406. X        av[i] = com + 1;
  407. X        while (*++com && *com != '\"');
  408. X        *com = '\0';
  409. X        break;
  410. X    case '$':
  411. X        if (*(com + 1) == '$') {
  412. X        av[i] = getenv(com + 2);
  413. X        if (av[i] == NULL) {
  414. X            printf ("Env. Var %s not found\n", com + 2);
  415. X            av[i] = com;
  416. X        }
  417. X#ifdef AMIGA
  418. X        av[i] = strcpy (mpush(strlen(av[i]) + 1), av[i]);
  419. X        ++pcount;
  420. X#endif
  421. X        }  else {
  422. X        av[i] = get_var (LEVEL_SET, com + 1);
  423. X        if (av[i] == NULL)
  424. X            av[i] = com;
  425. X        av[i] = strcpy (mpush(strlen(av[i]) + 1), av[i]);
  426. X        ++pcount;
  427. X        }
  428. X        break;
  429. X    default:
  430. X        av[i] = com;
  431. X        break;
  432. X    }
  433. X    ++i;
  434. X    }
  435. X    av[i] = NULL;
  436. X    ac = i;
  437. X    for (len = 0, i = 0; i < ac; ++i)
  438. X    len += strlen (av[i]) + 1;
  439. X    avline = mpush (len + 1);
  440. X    *avline = '\0';
  441. X    for (i = 0; i < ac; ++i) {
  442. X    strcat (avline, av[i]);
  443. X    if (i + 1 < ac)
  444. X        strcat (avline, " ");
  445. X    }
  446. X    if (XDebug)
  447. X    printf ("DEST: %s\n", avline);
  448. X    i = (*Command[ccno].func)(avline, Command[ccno].val);
  449. X    if (mpop() != avline)
  450. X    puts ("AVLINE ERROR");
  451. X    while (pcount--)
  452. X    mpop();
  453. X    fix();
  454. X    if (i < 0)
  455. X    return (i);
  456. X    if (flag < 2)
  457. X    goto loop;
  458. X    return (1);
  459. X}
  460. X
  461. X
  462. X/*
  463. X * BREAKOUT
  464. X *
  465. X * Breakout next argument.  If FLAG is set to 1 on return, the argument
  466. X * returned is the last in the command.  If FLAG is set to 2 on return, the
  467. X * argument returned is the last, period.
  468. X *
  469. X */
  470. X
  471. Xstatic char *
  472. Xbreakout(base, flag)
  473. Xint *flag;
  474. Xchar **base;
  475. X{
  476. X    register char *str, *scr;
  477. X
  478. Xloop:
  479. X    str = *base;            /* next start        */
  480. X    while (*str == ' ' || *str == 9)    /* skip spaces and such */
  481. X    ++str;
  482. X    switch (*str) {
  483. X    case '\0':                          /* no more arguments    */
  484. X    *flag = 2;
  485. X    *base = str;
  486. X    return (str);
  487. X    case ';':                           /* no more args in this command */
  488. X    *flag = 1;
  489. X    *str = '\0';
  490. X    *base = str + 1;
  491. X    return (str);
  492. X    }
  493. X    scr = str;
  494. X    for (;;) {                          /* valid argument of somesort   */
  495. X    switch (*scr) {
  496. X    case ' ':
  497. X    case 9:
  498. X        if (*flag >= 0)
  499. X        *scr = '\0';
  500. X        *base = scr + 1;
  501. X        *flag = 0;
  502. X        return (str);
  503. X    case '\"':
  504. X        ++scr;
  505. X        while (*scr && (*scr++ != '\"'));   /* place to end of quote */
  506. X        break;
  507. X    case '\0':
  508. X        *flag = 2;
  509. X        *base = scr;
  510. X        return (str);
  511. X    case ';':
  512. X        *flag = 1;
  513. X        *base = scr + 1;
  514. X        *scr = '\0';
  515. X        return (str);
  516. X    default:
  517. X        ++scr;
  518. X    }
  519. X    }
  520. X}
  521. X
  522. X
  523. X
  524. Xfix()
  525. X{
  526. X    register int i;
  527. X
  528. X    for (i = (Current < 0) ? 0 : Current; i < Entries; ++i) {
  529. X    if (Entry[i].no  &&  !(Entry[i].status & ST_DELETED)) {
  530. X        Current = i;
  531. X        return (1);
  532. X    }
  533. X    }
  534. X    if (Current >= Entries) {
  535. X    Current = Entries - 1;
  536. X    /* Can become -1 if no entries  */
  537. X    }
  538. X    for (i = Current; i >= 0; --i) {
  539. X    if (Entry[i].no  &&  !(Entry[i].status & ST_DELETED)) {
  540. X        Current = i;
  541. X        return (-1);
  542. X    }
  543. X    }
  544. X    Current = -1;
  545. X    return (-1);
  546. X}
  547. X
  548. X
  549. Xstatic
  550. Xfind_command(str, arg)
  551. Xchar *str;
  552. Xint arg;
  553. X{
  554. X    int i;
  555. X    int len = strlen (str);
  556. X
  557. X    if (*str >= '0'  &&  *str <= '9')
  558. X    return (0);
  559. X    for (i = 0; Command[i].func; ++i) {
  560. X    if (strncmp (str, Command[i].name, len) == 0) {
  561. X        if (arg == F_ABBR)
  562. X        return (i);
  563. X        if (strcmp (str, Command[i].name) == 0)
  564. X        return (i);
  565. X        return (-1);
  566. X    }
  567. X    }
  568. X    return (-1);
  569. X}
  570. X
  571. END_OF_FILE
  572. if test 12837 -ne `wc -c <'uucp2/src/dmail/execom.c'`; then
  573.     echo shar: \"'uucp2/src/dmail/execom.c'\" unpacked with wrong size!
  574. fi
  575. # end of 'uucp2/src/dmail/execom.c'
  576. fi
  577. if test -f 'uucp2/src/dmail/load_mail.c' -a "${1}" != "-c" ; then 
  578.   echo shar: Will not clobber existing file \"'uucp2/src/dmail/load_mail.c'\"
  579. else
  580. echo shar: Extracting \"'uucp2/src/dmail/load_mail.c'\" \(13945 characters\)
  581. sed "s/^X//" >'uucp2/src/dmail/load_mail.c' <<'END_OF_FILE'
  582. X
  583. X/*
  584. X *  LOAD_MAIL.C
  585. X *
  586. X *  $Header: Beta:src/uucp/src/dmail/RCS/load_mail.c,v 1.1 90/02/02 12:03:35 dillon Exp Locker: dillon $
  587. X *
  588. X *  (C) Copyright 1985-1990 by Matthew Dillon,  All Rights Reserved.
  589. X *
  590. X *  file-io routines to scan the mail file and load required information.
  591. X *
  592. X *
  593. X *  Global Routines:    HOLD_LOAD()         hold on loading mail after change
  594. X *            NOHOLD_LOAD()       hold off.. load if changes
  595. X *            LOAD_CHANGES()      reload mail if changed
  596. X *            LOAD_MAIL()         load/reload mail
  597. X *            SAVE_FILE()         save mail items back to spool
  598. X *            CHECK_NEW_MAIL()    check for new mail
  599. X *            WRITE_FILE()        append mail items to a file
  600. X *            GET_EXTRA_OVR()     ret index of Field (create if not)
  601. X *            ADD_EXTRA()         add another field (reloads mail)
  602. X *            DELETE_EXTRA()      delete a field
  603. X *            GET_EXTRA()         ret index of Field, or error
  604. X *            M_SELECT()          select on current message list
  605. X *
  606. X *
  607. X *  Static Routines:    LOAD_HASH()         load hash table from fields list
  608. X *            FREE_ENTRY()        unload EVERYTHING
  609. X *            FREE_TABLE()        unload all Fields table
  610. X *            LOAD_FILE()         raw file loading/counting
  611. X *
  612. X *
  613. X */
  614. X
  615. X#include <stdio.h>
  616. X#include <sys/file.h>
  617. X#include "dmail.h"
  618. X
  619. Xvoid do_flock();
  620. Xvoid free_table();
  621. Xvoid load_hash();
  622. X
  623. X#define NOHOLD    0
  624. X#define HOLD    1
  625. X
  626. X#define NO_BASE     0
  627. X#define NO_FIELDS   1
  628. X#define ENTRY_OK    2
  629. X
  630. Xstruct FIND Find[MAXTYPE + 1] = {
  631. X    "From:"   , 5, 1, 0,
  632. X    "To:"     , 3, 1, 0,
  633. X    "Subject:", 8, 1, 0 };
  634. X
  635. Xstatic int  File_size;
  636. Xstatic int  changed, load_hold;
  637. Xstatic int  Hash[256];
  638. X
  639. Xstatic char *quo_quo = "";
  640. X
  641. Xvoid
  642. Xhold_load()
  643. X{
  644. X    load_hold = 1;
  645. X}
  646. X
  647. Xvoid
  648. Xnohold_load()
  649. X{
  650. X    void load_changes();
  651. X    load_hold = 0;
  652. X    load_changes();
  653. X}
  654. X
  655. Xvoid
  656. Xload_changes()
  657. X{
  658. X    if (changed  &&  !load_hold)
  659. X    load_mail(Entries, 1);
  660. X}
  661. X
  662. Xinitial_load_mail()
  663. X{
  664. X    if (load_mail (0, 0) < 0)
  665. X    return (-1);
  666. X    return ((Entries) ? 1 : -1);
  667. X}
  668. X
  669. X
  670. Xstatic
  671. Xload_mail(at, from0)
  672. X{
  673. X    FILE *fi;
  674. X    int i, count, file_size;
  675. X
  676. X    if (No_load_mail)
  677. X    return (-1);
  678. X    push_break();
  679. X    load_hash();
  680. X    if (from0)
  681. X    free_table (0, HOLD);
  682. X    else
  683. X    free_table (at, NOHOLD);
  684. X    fi = fopen (mail_file, "r+");
  685. X    if (m_fi != NULL)
  686. X    fclose (m_fi);
  687. X    m_fi = fopen (mail_file, "r+");
  688. X    if (fi == NULL  ||  m_fi == NULL) {
  689. X    pop_break();
  690. X    return (-1);
  691. X    }
  692. X    do_flock (fileno(m_fi), LOCK_EX);
  693. X    if (at)
  694. X    fseek (fi, Entry[at].fpos, 0);
  695. X    else
  696. X    fseek (fi, 0, 0);
  697. X    count = Entries;
  698. X    while (search_from(fi))
  699. X    ++count;
  700. X    if (Entries != count) {
  701. X    if (!lmessage_overide)
  702. X        printf ("%d Other Items loaded\n", count - Entries);
  703. X    lmessage_overide = 0;
  704. X    Entry = (struct ENTRY *)realloc (Entry, sizeof(*Entry) * (count + 1));
  705. X    bzero (&Entry[Entries], sizeof(*Entry) * (count + 1 - Entries));
  706. X    }
  707. X    Entries = count;
  708. X    for (i = at; i < Entries; ++i) {
  709. X    Entry[i].no  = 0;
  710. X    Entry[i].status = 0;
  711. X    }
  712. X    Entry[i].fpos = File_size = file_size = ftell (fi);
  713. X    fclose (fi);
  714. X    load_file ((from0) ? 0 : at);
  715. X    if (file_size != File_size) {       /* Last entry incomplete?       */
  716. X    free_table (Entries - 1, NOHOLD);
  717. X    }
  718. X    changed = 0;
  719. X    if (SelAll)
  720. X    m_select (Nulav, 0);
  721. X    flock (fileno(m_fi), LOCK_UN);
  722. X    pop_break();
  723. X    return (1);
  724. X}
  725. X
  726. Xvoid
  727. Xdo_flock(fd, stat)
  728. X{
  729. X    if (flock(fd, stat | LOCK_NB) < 0) {
  730. X    puts ("File in use, Waiting for lock");
  731. X    flock (fd, stat);
  732. X    puts ("Have lock");
  733. X    }
  734. X}
  735. X
  736. Xstatic
  737. Xload_file(at)
  738. Xint at;
  739. X{
  740. X    FILE *fi;
  741. X    char *next, *ptr;
  742. X    int i, bit, maxbit, len, count, havefrom;
  743. X
  744. X    maxbit = 0;
  745. X    for (i = 0; Find[i].search != NULL; ++i)
  746. X    maxbit = (maxbit << 1) | 1;
  747. X    fi = fopen (mail_file, "r");
  748. X    count = -1;
  749. X    havefrom = 0;
  750. X    while (havefrom  ||  search_from (fi)) {
  751. X    havefrom = 0;
  752. X    if (++count >= Entries)
  753. X        break;
  754. X    len = strlen(Buf) - 1;
  755. X    Buf[len] = '\0';
  756. X    next = next_word(Buf);
  757. X    len -= next - Buf;
  758. X    Entry[count].fpos = ftell (fi);
  759. X    Entry[count].from = malloc (len + 1);
  760. X    bcopy (next, Entry[count].from, len + 1);
  761. X
  762. X    /* SEARCH FIELD LIST */
  763. X
  764. X    bit = 0;
  765. X    if (XDebug)
  766. X        printf ("No %d  ---------------------\n", count + 1);
  767. X    while (fgets (Buf, MAXFIELDSIZE, fi) != NULL) {
  768. X        if (Buf[0] == '\n')
  769. X        break;
  770. X        if (isfrom(Buf)) {
  771. X        havefrom = 1;
  772. X        break;
  773. X        }
  774. X        len = strlen(Buf) - 1;
  775. X        Buf[len] = '\0';
  776. X        if (XDebug)
  777. X        printf ("CHECK: %s\n", Buf);
  778. X        next = next_word(Buf);
  779. X        len -= next - Buf;
  780. X        if (Hash[*Buf] == 0)
  781. X        continue;
  782. X        if (Hash[*Buf] > 0) {
  783. X        i = Hash[*Buf] & 0xff;
  784. X        if (strncmp (Find[i].search, Buf, Find[i].len) == 0)
  785. X            goto found;
  786. X        continue;
  787. X        }
  788. X        for (i = -Hash[*Buf] & 0xff; Find[i].search; ++i) {
  789. X        if (*Find[i].search != *Buf)
  790. X            break;
  791. X        if (strncmp (Find[i].search, Buf, Find[i].len) == 0)
  792. X            goto found;
  793. X        }
  794. X        continue;
  795. Xfound:
  796. X        if (XDebug)
  797. X        printf ("Found: %d %s\n", i, Buf);
  798. X        if (Find[i].notnew == 0) {
  799. X        Find[i].notnew = 1;
  800. X        ptr = Buf;
  801. X        while (*ptr  &&  *ptr != ':')
  802. X            ++ptr;
  803. X        ++ptr;
  804. X        Find[i].search =
  805. X            realloc (Find[i].search, ptr - Buf + 1);
  806. X        strncpy (Find[i].search, Buf, ptr - Buf);
  807. X        *(Find[i].search + (ptr - Buf)) = '\0';
  808. X        Find[i].len = strlen(Find[i].search);
  809. X        }
  810. X        compile_field (Buf, fi);
  811. X        Entry[count].fields[i] =
  812. X            malloc (strlen(next) + 1);
  813. X        strcpy (Entry[count].fields[i], next);
  814. X        if ((bit |= (1 << i)) == maxbit)
  815. X        break;
  816. X    }
  817. X    if (bit != maxbit) {
  818. X        for (i = 0; Find[i].search != NULL; ++i) {
  819. X        if (((1 << i) & bit) == 0) {
  820. X            Entry[count].fields[i] = quo_quo;
  821. X        }
  822. X        }
  823. X    }
  824. X    }
  825. X    File_size = ftell (fi);
  826. X    fclose (fi);
  827. X    return (1);
  828. X}
  829. X
  830. X
  831. Xstatic void
  832. Xload_hash()
  833. X{
  834. X    register int i, c;
  835. X
  836. X    bzero (Hash, sizeof(Hash));
  837. X    for (i = 0; Find[i].search; ++i) {
  838. X    c = *Find[i].search;
  839. X    if (Hash[c] > 0)
  840. X        Hash[c] = -Hash[c];
  841. X    if (Hash[c] == 0)
  842. X        Hash[c] = i | 0x100;
  843. X    }
  844. X}
  845. X
  846. X
  847. Xvoid
  848. Xfree_entry()
  849. X{
  850. X    free_table(0, NOHOLD);
  851. X    Entry = (struct ENTRY *)realloc (Entry, sizeof(*Entry));
  852. X    bzero (Entry[0].fields, sizeof(Entry[0].fields));
  853. X    File_size = Entries = 0;
  854. X    Entry->status = Entry->no = Entry->fpos = Current = 0;
  855. X    Listsize = 3;
  856. X    if (m_fi) {
  857. X    fclose (m_fi);
  858. X    m_fi = NULL;
  859. X    }
  860. X}
  861. X
  862. X
  863. Xstatic void
  864. Xfree_table(at, hold)
  865. X{
  866. X    int i, j;
  867. X
  868. X    for (i = at; i < Entries; ++i) {
  869. X    xfree (Entry[i].from);
  870. X    for (j = 0; Find[j].search != NULL; ++j) {
  871. X        if (Entry[i].fields[j] != quo_quo)
  872. X        xfree (Entry[i].fields[j]);
  873. X    }
  874. X    }
  875. X    Entries = (hold == HOLD) ? Entries : at;
  876. X    File_size = (at) ? Entry[Entries].fpos : 0;
  877. X}
  878. X
  879. Xstatic
  880. Xsearch_from(fi)
  881. XFILE *fi;
  882. X{
  883. X    while (fgets (Buf, MAXFIELDSIZE, fi) != NULL) {
  884. X    if (isfrom (Buf))
  885. X        return (1);
  886. X    }
  887. X    return (0);
  888. X}
  889. X
  890. X
  891. Xsave_file(reload, mark, notmark)
  892. X{
  893. X    FILE *fiscr;
  894. X    int fdscr;
  895. X    int i, count;
  896. X    char scratch[64];
  897. X
  898. X    for (i = 0; i < Entries; ++i) {
  899. X    if ((Entry[i].status & mark) != mark  ||
  900. X        (~Entry[i].status & notmark) != notmark)
  901. X        break;
  902. X    }
  903. X    if (i == Entries) {
  904. X    m_select (Nulav, M_RESET);
  905. X    puts ("No Changes Made");
  906. X    return (Entries);
  907. X    }
  908. X    if (m_fi == NULL)
  909. X    return (-1);
  910. X    count = 0;
  911. X    sprintf(scratch, "t:dmail%d", getpid());
  912. X    do_flock (fileno(m_fi), LOCK_EX);
  913. X    fdscr = open (scratch, O_RDWR | O_CREAT | O_TRUNC, MAILMODE);
  914. X#ifdef AMIGA        /*    fix bug in Lattice C fdopen */
  915. X    fiscr = fopen("nil:", "w");
  916. X    fclose(fiscr);
  917. X#endif
  918. X    fiscr = fdopen (fdscr, "a+");
  919. X    for (i = 0; i < Entries; ++i) {
  920. X    if ((Entry[i].status & mark) == mark  &&
  921. X        (~Entry[i].status & notmark) == notmark) {
  922. X        ++count;
  923. X        fputs ("From ", fiscr);
  924. X        fputs (Entry[i].from, fiscr);
  925. X        putc ('\n', fiscr);
  926. X        fseek (m_fi, Entry[i].fpos, 0);
  927. X        while (fgets (Buf, MAXFIELDSIZE, m_fi) != NULL) {
  928. X        if (isfrom(Buf))
  929. X            break;
  930. X        fputs (Buf, fiscr);
  931. X        }
  932. X    }
  933. X    }
  934. X
  935. X    /*
  936. X     *    If new mail has come in, append to the scratch file as well.
  937. X     *    NOTE: for some machines like the Amiga an already open descriptor
  938. X     *          does not know about any new data, thus we cannot simply
  939. X     *          use m_fi .
  940. X     */
  941. X
  942. X    {
  943. X    FILE *fi;
  944. X
  945. X    if (fi = fopen(mail_file, "r")) {
  946. X        fseek(fi, File_size, 0);
  947. X        while (fgets(Buf, MAXFIELDSIZE, fi))
  948. X        fputs(Buf, fiscr);
  949. X        fclose(fi);
  950. X    }
  951. X    }
  952. X
  953. X    /* Write scratch file back to mail file, or try to */
  954. X
  955. X    fflush (fiscr);
  956. X    fflush (m_fi);
  957. X
  958. X    lseek (fdscr, 0 ,0);
  959. X#ifdef UNIX
  960. X    lseek (fileno(m_fi), 0, 0);
  961. X    while ((i = read (fdscr, Buf, MAXFIELDSIZE)) > 0)
  962. X    write (fileno(m_fi), Buf, i);
  963. X    ftruncate (fileno(m_fi), lseek (fileno(m_fi), 0, 1));
  964. X#else
  965. X    fclose(m_fi);
  966. X    if (m_fi = fopen (mail_file, "w")) {
  967. X    while ((i = read (fdscr, Buf, MAXFIELDSIZE)) > 0)
  968. X        write (fileno(m_fi), Buf, i);
  969. X    fclose(m_fi);
  970. X    m_fi = fopen (mail_file, "r+");
  971. X    }
  972. X    if (m_fi == NULL) {
  973. X    printf("Unable to re-open %s !\n", mail_file);
  974. X    return(-1);
  975. X    }
  976. X#endif
  977. X    if (lseek (fileno(m_fi), 0, 2) == 0  &&  !reload) {
  978. X    if (Did_cd == 0) {
  979. X        fclose(m_fi);
  980. X        m_fi = NULL;
  981. X        if (unlink (mail_file) == 0)
  982. X        printf ("%s  Removed\n", mail_file);
  983. X        else
  984. X        printf ("0 messages left in %s\n", mail_file);
  985. X    }
  986. X    }
  987. X    fclose (fiscr);
  988. X    if (m_fi)
  989. X    fclose (m_fi);          /* Effectively unlocks the descriptor */
  990. X    m_fi = NULL;
  991. X    unlink (scratch);
  992. X    if (reload) {
  993. X    free_entry();
  994. X    load_mail(0, 0);
  995. X    }
  996. X    m_select (Nulav, M_RESET);
  997. X    return (count);
  998. X}
  999. X
  1000. Xvoid
  1001. Xcheck_new_mail()
  1002. X{
  1003. X    FILE *fi;
  1004. X
  1005. X    push_break();
  1006. X    if (m_fi == NULL) {
  1007. X    m_fi = fopen (mail_file, "r+");
  1008. X    if (m_fi == NULL) {
  1009. X        pop_break();
  1010. X        return;
  1011. X    }
  1012. X    }
  1013. X    if (fi = fopen(mail_file, "r")) {
  1014. X    if (fseek(fi, 0, 2) < 0 || ftell(fi) != File_size)
  1015. X        load_mail(Entries, 1);
  1016. X    fclose(fi);
  1017. X    }
  1018. X    pop_break();
  1019. X}
  1020. X
  1021. X
  1022. Xwrite_file(file, modes, mark, notmark)
  1023. Xchar *file;
  1024. X{
  1025. X    int i, fd = 1, notopen = 1;
  1026. X    FILE *fi = NULL;
  1027. X
  1028. X    for (i = 0; i < Entries; ++i) {
  1029. X    if ((Entry[i].status & mark) == mark  &&
  1030. X        (~Entry[i].status & notmark) == notmark) {
  1031. X        if (notopen) {
  1032. X        notopen = 0;
  1033. X        fd = open (file, O_APPEND | O_WRONLY | modes, MAILMODE);
  1034. X        if (fd < 0)
  1035. X            return (-1);
  1036. X        do_flock (fd, LOCK_EX);
  1037. X#ifdef AMIGA        /*    fix bug in Lattice C fdopen */
  1038. X        fi = fopen("nil:", "w");
  1039. X        fclose(fi);
  1040. X#endif
  1041. X        fi = fdopen (fd, "a");
  1042. X
  1043. X#ifdef NOTDEF
  1044. X        if (fi) {
  1045. X            printf("ptr     %08lx\n", fi->_ptr);
  1046. X            printf("rcnt    %08lx\n", fi->_rcnt);
  1047. X            printf("wcnt    %08lx\n", fi->_wcnt);
  1048. X            printf("base    %08lx\n", fi->_base);
  1049. X            printf("size    %08lx\n", fi->_size);
  1050. X            printf("flag    %08lx\n", fi->_flag);
  1051. X            printf("file    %08lx\n", fi->_file);
  1052. X            return(-1);
  1053. X        }
  1054. X#endif
  1055. X        }
  1056. X        fputs ("From ", fi);
  1057. X        fputs (Entry[i].from, fi);
  1058. X        putc ('\n', fi);
  1059. X        if (m_fi) {
  1060. X        fseek (m_fi, Entry[i].fpos, 0);
  1061. X        while (fgets (Buf, MAXFIELDSIZE, m_fi) != NULL) {
  1062. X            if (isfrom(Buf))
  1063. X            break;
  1064. X            fputs (Buf, fi);
  1065. X        }
  1066. X        }
  1067. X    }
  1068. X    }
  1069. X    if (!notopen)
  1070. X    fclose (fi);
  1071. X    return (1);
  1072. X}
  1073. X
  1074. X/*
  1075. X * Basic scheme: Each entry has a fields list.    Each entry in the fields list
  1076. X * is guarenteed to be a valid malloc'd pointer (except some may be set to
  1077. X * quo_quo).
  1078. X *
  1079. X * The find[] struct array holds the field name and length, the index
  1080. X * corresponding to the index into the field[] in an Entry.
  1081. X *
  1082. X * The header and width arrays hold the list format.
  1083. X */
  1084. X
  1085. Xget_extra_ovr(str)
  1086. Xchar *str;
  1087. X{
  1088. X    register int i;
  1089. X
  1090. X    i = get_extra (str);
  1091. X    if (i < 0) {
  1092. X    i = add_extra (str);
  1093. X    load_changes();
  1094. X    }
  1095. X    return (i);
  1096. X}
  1097. X
  1098. X
  1099. X/*
  1100. X * If there's room to add it, append to end.
  1101. X * Else Find oldest field which doesn't exist in the setlist and replace it
  1102. X *  with the new one.
  1103. X */
  1104. X
  1105. Xadd_extra(str)
  1106. Xchar *str;
  1107. X{
  1108. X    register int i, j, j_age, k;
  1109. X
  1110. X    for (i = EXSTART; i < MAXTYPE; ++i) {
  1111. X    if (Find[i].search == NULL)
  1112. X        break;
  1113. X    ++Find[i].age;
  1114. X    }
  1115. X    if (i == MAXTYPE) {                 /* No room to add onto end */
  1116. X    j = j_age = -1;
  1117. X    for (i = EXSTART; i < MAXTYPE; ++i) {
  1118. X        for (k = 0; k < Listsize; ++k) {
  1119. X        if (i == header[k])
  1120. X            break;
  1121. X        }
  1122. X        if (k == Listsize  &&  Find[i].age > j_age) {
  1123. X        j = i;
  1124. X        j_age = Find[i].age;
  1125. X        }
  1126. X    }
  1127. X    i = j;
  1128. X    }
  1129. X    if (i < 0)
  1130. X    return (-1);
  1131. X    push_break();
  1132. X    if (Find[i].search != NULL)
  1133. X    xfree (Find[i].search);
  1134. X    Find[i].len = strlen(str);
  1135. X    Find[i].search = malloc (Find[i].len + 1);
  1136. X    Find[i].notnew = Find[i].age = 0;
  1137. X    strcpy (Find[i].search, str);
  1138. X    changed = 1;
  1139. X    for (j = 0; j < Entries; ++j) {
  1140. X    if (Entry[j].fields[i] && Entry[j].fields[i] != quo_quo)
  1141. X        xfree (Entry[j].fields[i]);
  1142. X    Entry[j].fields[i] = quo_quo;
  1143. X    }
  1144. X    pop_break();
  1145. X    return (i);
  1146. X}
  1147. X
  1148. X
  1149. Xget_extra(str)
  1150. Xchar *str;
  1151. X{
  1152. X    int i;
  1153. X
  1154. X    for (i = 0; Find[i].search; ++i) {
  1155. X    if (strncmp (str, Find[i].search, strlen(str)) == 0) {
  1156. X        Find[i].age = 0;
  1157. X        return (i);
  1158. X    }
  1159. X    }
  1160. X    return (-1);
  1161. X}
  1162. X
  1163. X
  1164. Xm_select(sav, mode)
  1165. Xregister char *sav[];
  1166. X{
  1167. X    char *ptr, *dest;
  1168. X    char l_map[256];
  1169. X    int idx[MAXLIST], ix = 0;
  1170. X    int ok, not, len, scr;
  1171. X    register int i, j, avi;
  1172. X
  1173. X    for (i = 0;i < 256; ++i)
  1174. X    l_map[i] = i;
  1175. X    for (i = 'A'; i <= 'Z'; ++i)
  1176. X    l_map[i] += 'a' - 'A';
  1177. X    hold_load();
  1178. X    i = 0;
  1179. X    idx[ix++] = get_extra_ovr (sav[i++]);
  1180. X    for (; sav[i]; ++i) {
  1181. X    if (strcmp (sav[i], ",") == 0  &&  sav[i + 1])
  1182. X        idx[ix++] = get_extra_ovr (sav[++i]);
  1183. X    }
  1184. X    idx[ix] = -1;
  1185. X    nohold_load();
  1186. X    j = 1;
  1187. X    push_break();
  1188. X    for (i = 0; i < Entries; ++i) {
  1189. X    if (mode == M_CONT  &&  Entry[i].no == 0)
  1190. X        continue;
  1191. X    ix = ok = 0;
  1192. X    avi = 1;
  1193. X    while ((ptr = sav[avi]) != NULL) {
  1194. X        if (ptr[0] == ','  &&  ptr[1] == '\0' && sav[avi+1]) {
  1195. X        ++ix;
  1196. X        avi += 2;
  1197. X        continue;
  1198. X        }
  1199. X        if (not = (*ptr == '!'))
  1200. X        ++ptr;
  1201. X        len = strlen (ptr);
  1202. X        dest = Entry[i].fields[idx[ix]];
  1203. X        if (*ptr == '\0') {
  1204. X        ok = 1;
  1205. X        goto gotit;
  1206. X        }
  1207. X        while (*dest) {
  1208. X        scr = 0;
  1209. X        while (l_map[dest[scr]] == l_map[ptr[scr]] && ptr[scr])
  1210. X            ++scr;
  1211. X        if (ptr[scr] == '\0') {
  1212. X            ok = 1;
  1213. X            goto gotit;
  1214. X        }
  1215. X        ++dest;
  1216. X        }
  1217. X        ++avi;
  1218. X    }
  1219. Xgotit:
  1220. X    Entry[i].no = (ok ^ not) ? j++ : 0;
  1221. X    }
  1222. X    pop_break();
  1223. X    if (Current < 0)
  1224. X    Current = 0;
  1225. X    if (Entries) {
  1226. X    if (Entry[Current].no == 0) {
  1227. X        Current = indexof (1);
  1228. X        if (Current < 0) {
  1229. X         Current = 0;
  1230. X         return (-1);
  1231. X        }
  1232. X    }
  1233. X    } else {
  1234. X    Current = -1;
  1235. X    }
  1236. X    return (1);
  1237. X}
  1238. X
  1239. X
  1240. END_OF_FILE
  1241. if test 13945 -ne `wc -c <'uucp2/src/dmail/load_mail.c'`; then
  1242.     echo shar: \"'uucp2/src/dmail/load_mail.c'\" unpacked with wrong size!
  1243. fi
  1244. # end of 'uucp2/src/dmail/load_mail.c'
  1245. fi
  1246. if test -f 'uucp2/src/uuser/uuser.c' -a "${1}" != "-c" ; then 
  1247.   echo shar: Will not clobber existing file \"'uucp2/src/uuser/uuser.c'\"
  1248. else
  1249. echo shar: Extracting \"'uucp2/src/uuser/uuser.c'\" \(13776 characters\)
  1250. sed "s/^X//" >'uucp2/src/uuser/uuser.c' <<'END_OF_FILE'
  1251. X
  1252. X/*
  1253. X *  UUSER.C UUSER:devicename/unitnumber/options
  1254. X *        UUSER:serial.device/0/R1000
  1255. X *
  1256. X *  $Header: Beta:src/uucp/src/uuser/RCS/uuser.c,v 1.1 90/02/02 12:10:15 dillon Exp Locker: dillon $
  1257. X *
  1258. X *  (C) Copyright 1989-1990 by Matthew Dillon,  All Rights Reserved.
  1259. X *
  1260. X *  options:
  1261. X *    Rn    Set read timeout when no data available to n millisecs
  1262. X *    C0    Ignore carrier detect
  1263. X *
  1264. X *  features:
  1265. X *    1K asynchronous write capability (write 0 bytes to sync up)
  1266. X *    programmable read-timeout (Getty starts procs up w/ 1sec to)
  1267. X *
  1268. X *    combined together, you can easily implement 100% efficient
  1269. X *    protocols even with that 1 second read timeout!
  1270. X */
  1271. X
  1272. X#include <exec/types.h>
  1273. X#include <exec/memory.h>
  1274. X#include <devices/serial.h>
  1275. X#include <devices/timer.h>
  1276. X#include <libraries/dos.h>
  1277. X#include <libraries/dosextens.h>
  1278. X#include <libraries/filehandler.h>
  1279. X#include <hardware/cia.h>
  1280. X#include <stdio.h>
  1281. X#include "protos.h"
  1282. X#include "version.h"
  1283. X
  1284. XIDENT(".02");
  1285. X
  1286. X#define WRITEBUFSIZE    1024
  1287. X#define MPC (MEMF_PUBLIC|MEMF_CLEAR)        /* options to AllocMem()    */
  1288. X
  1289. X#define BTOC(x, type)   ((type *)((long)x << 2))
  1290. X#define CTOB(x)         ((BPTR)((long)x >> 2))
  1291. X
  1292. X#define DOS_FALSE    0L
  1293. X#define DOS_TRUE     -1L
  1294. X
  1295. Xtypedef struct IOExtSer     IOS;
  1296. Xtypedef struct timerequest  IOT;
  1297. Xtypedef struct IORequest    IOR;
  1298. Xtypedef struct timeval        TimeVal;
  1299. X
  1300. Xtypedef struct FileLock     LOCK;
  1301. Xtypedef struct DosPacket    Packet;
  1302. Xtypedef struct Process        PROC;
  1303. Xtypedef struct DeviceNode   DEVNODE;
  1304. Xtypedef struct FileHandle   FH;
  1305. Xtypedef struct Message        MSG;
  1306. Xtypedef struct Node        NODE;
  1307. Xtypedef struct List        LIST;
  1308. Xtypedef struct MsgPort        PORT;
  1309. X
  1310. Xtypedef struct SHandle {
  1311. X    NODE    Node;
  1312. X    IOT     Iot;        /* wait-for-char and read req    */
  1313. X    IOS     Ios;
  1314. X    IOS     Iosr;        /* 1005             */
  1315. X    IOS     Iosw;        /* 1005,1006            */
  1316. X    char    IotIP;
  1317. X    char    IosrIP;
  1318. X    char    IoswIP;
  1319. X    char    RxIn[1];        /* one char buffer        */
  1320. X    char    *TxOut;        /* asynch write buffer        */
  1321. X    Packet  *RPacket;        /* current pending read packet    */
  1322. X    Packet  *WPacket;        /* current pending write packet */
  1323. X    short   Flags;
  1324. X    LIST    RxWait;        /* requests waiting for data    */
  1325. X    LIST    TxWait;        /* requests waiting to write    */
  1326. X    LIST    CxWait;        /* wait for char        */
  1327. X    TimeVal ReadTo;
  1328. X} SHandle;
  1329. X
  1330. X#define HF_IGNORECD    0x01
  1331. X#define HF_CDLOST    0x02
  1332. X#define HF_RTO        0x04
  1333. X#define HF_DONTLOCK    0x08    /* if G option for from-getty    */
  1334. X
  1335. Xextern Packet *taskwait();      /* wait for a message           */
  1336. X
  1337. X/* long SysBase;        /* required to make Exec calls  */
  1338. Xchar ScrBuf[256];        /* Scratch buffer        */
  1339. XPORT *IoSink;
  1340. XLIST HanList;
  1341. XLIST NodList;
  1342. XIOT  Iot;            /* Iot master, also used for CD */
  1343. Xchar IotIP;
  1344. X
  1345. Xvoid    AttemptRead();
  1346. Xvoid    AttemptWrite();
  1347. Xvoid    AbortPackets();
  1348. Xvoid    StartTimer();
  1349. X
  1350. Xvoid
  1351. X_main()
  1352. X{
  1353. X    PROC    *myproc;
  1354. X    DEVNODE    *mynode;
  1355. X    UBYTE    notdone;
  1356. X    long    mask;
  1357. X
  1358. X/*    SysBase = *(long *)4;  */
  1359. X    IoSink = CreatePort(NULL, 0L);
  1360. X    NewList(&HanList);
  1361. X    NewList(&NodList);
  1362. X
  1363. X    Iot.tr_node.io_Message.mn_ReplyPort = IoSink;
  1364. X    OpenDevice("timer.device", UNIT_VBLANK, &Iot.tr_node, 0L);
  1365. X    Iot.tr_node.io_Command = TR_ADDREQUEST;
  1366. X    Iot.tr_time.tv_secs = 4;
  1367. X    Iot.tr_time.tv_micro= 0;
  1368. X    SendIO(&Iot);
  1369. X    IotIP = 1;
  1370. X
  1371. X    myproc  = (PROC *)FindTask(0L);
  1372. X
  1373. X    /*
  1374. X     * INITIAL STARTUP MESSAGE
  1375. X     */
  1376. X
  1377. X    {
  1378. X    Packet *mypkt;
  1379. X
  1380. X    mypkt    = taskwait(myproc);
  1381. X    mynode    = BTOC(mypkt->dp_Arg3, DEVNODE);
  1382. X    mynode->dn_Task = &myproc->pr_MsgPort;
  1383. X    returnpkt(mypkt, myproc, DOS_TRUE, 0L);
  1384. X    }
  1385. X
  1386. Xloop:
  1387. X    notdone = 1;
  1388. X    mask = (1 << IoSink->mp_SigBit) | (1 << myproc->pr_MsgPort.mp_SigBit);
  1389. X    while (notdone) {
  1390. X    Packet *mypkt;         /* dos packet received      */
  1391. X    IOR *ior;
  1392. X    SHandle *handle;
  1393. X    long type;            /* type of packet        */
  1394. X
  1395. X    ior = (IOR *)GetMsg(IoSink);
  1396. X    mypkt = (Packet *)GetMsg(&myproc->pr_MsgPort);
  1397. X    if (mypkt)
  1398. X        mypkt = (Packet *)(((MSG *)mypkt)->mn_Node.ln_Name);
  1399. X    while (mypkt == NULL && ior == NULL) {
  1400. X        Wait(mask);
  1401. X        ior = (IOR *)GetMsg(IoSink);
  1402. X        mypkt = (Packet *)GetMsg(&myproc->pr_MsgPort);
  1403. X        if (mypkt)
  1404. X        mypkt = (Packet *)(((MSG *)mypkt)->mn_Node.ln_Name);
  1405. X    }
  1406. X
  1407. X    /*
  1408. X     *  Make sure there is at least one free node in node list
  1409. X     */
  1410. X
  1411. X    if (NodList.lh_Head == (NODE *)&NodList.lh_Tail) {
  1412. X        NODE *pknode = AllocMem(sizeof(NODE), MPC);
  1413. X        AddTail(&NodList, pknode);
  1414. X    }
  1415. X
  1416. X    /*
  1417. X     *  Returned IO request, sift through lists to find it.
  1418. X     */
  1419. X
  1420. X    if (ior) {
  1421. X        if (ior == &Iot.tr_node) {      /*  Check for carrier lost */
  1422. X        for (handle = (SHandle *)HanList.lh_Head; handle != (SHandle *)&HanList.lh_Tail; handle = (SHandle *)handle->Node.ln_Succ) {
  1423. X            if (!(handle->Flags & HF_IGNORECD) && !(handle->Flags & HF_CDLOST)) {
  1424. X            handle->Ios.IOSer.io_Command = SDCMD_QUERY;
  1425. X            DoIO((IOR *)&handle->Ios);
  1426. X            if (handle->Ios.io_Status & CIAF_COMCD) {
  1427. X                handle->Flags |= HF_CDLOST;
  1428. X                AbortPackets(handle, myproc);
  1429. X            }
  1430. X            }
  1431. X        }
  1432. X        if (HanList.lh_Head == (NODE *)&HanList.lh_Tail) {
  1433. X            IotIP = 0;
  1434. X        } else {
  1435. X            Iot.tr_time.tv_secs = 4;
  1436. X            Iot.tr_time.tv_micro= 0;
  1437. X            SendIO(&Iot.tr_node);
  1438. X            IotIP = 1;
  1439. X        }
  1440. X        } else
  1441. X        for (handle = (SHandle *)HanList.lh_Head; handle != (SHandle *)&HanList.lh_Tail; handle = (SHandle *)handle->Node.ln_Succ) {
  1442. X        if (ior == (IOR *)&handle->Iosr) {
  1443. X            handle->IosrIP = 0;
  1444. X            if (handle->RPacket) {
  1445. X            returnpkt(handle->RPacket, myproc, handle->Iosr.IOSer.io_Actual, 0L);
  1446. X            handle->RPacket = NULL;
  1447. X            }
  1448. X            if (handle->IotIP) {
  1449. X            AbortIO(&handle->Iot.tr_node);
  1450. X            WaitIO(&handle->Iot.tr_node);
  1451. X            handle->IotIP = 0;
  1452. X            }
  1453. X            AttemptRead(handle, myproc);
  1454. X        }
  1455. X        if (ior == (IOR *)&handle->Iosw) {
  1456. X            handle->IoswIP = 0;
  1457. X            if (handle->WPacket) {
  1458. X            handle->Iosw.IOSer.io_Data = (APTR)handle->TxOut;
  1459. X            handle->Iosw.IOSer.io_Length= handle->WPacket->dp_Arg3 - handle->WPacket->dp_Res1;
  1460. X            SendIO((IOR *)&handle->Iosw);
  1461. X            handle->IoswIP = 1;
  1462. X            handle->WPacket->dp_Res1 = handle->WPacket->dp_Arg3;
  1463. X            returnpktplain(handle->WPacket, myproc);
  1464. X            handle->WPacket = NULL;
  1465. X            } else {
  1466. X            AttemptWrite(handle, myproc);
  1467. X            }
  1468. X        }
  1469. X        if (ior == (IOR *)&handle->Iot.tr_node) {
  1470. X            handle->IotIP = 0;
  1471. X            if ((handle->Flags & HF_RTO) && handle->IosrIP) {
  1472. X            AbortIO((IOR *)&handle->Iosr);
  1473. X            }
  1474. X        }
  1475. X        }
  1476. X    }
  1477. X
  1478. X    if (mypkt) {
  1479. X        mypkt->dp_Res1 = DOS_TRUE;    /* default return value     */
  1480. X        mypkt->dp_Res2 = 0;     /* default no error        */
  1481. X        type = mypkt->dp_Type;    /* packet type            */
  1482. X
  1483. X        /*
  1484. X         *    Extract pipe pointer (only applies to read/write)
  1485. X         */
  1486. X
  1487. X        handle = (SHandle *)mypkt->dp_Arg1;     /*  READ/WRITE only */
  1488. X
  1489. X        switch(type) {
  1490. X        case ACTION_FINDINPUT:
  1491. X        case ACTION_FINDOUTPUT:
  1492. X        case ACTION_FINDUPDATE:
  1493. X        if (IotIP == 0)
  1494. X            StartTimer(4);
  1495. X        {
  1496. X            FH     *fh = BTOC(mypkt->dp_Arg1, FH);
  1497. X            char *path = BTOC(mypkt->dp_Arg3, char);
  1498. X            char *unit;
  1499. X            long n;
  1500. X
  1501. X            movmem(path + 1, ScrBuf, *path);
  1502. X            ScrBuf[*path] = 0;
  1503. X            path = ScrBuf;
  1504. X
  1505. X            handle = AllocMem(sizeof(SHandle), MPC);
  1506. X
  1507. X            if (strcmp(ScrBuf, "*") == 0)
  1508. X            strcpy(ScrBuf, "serial.device/0");
  1509. X            for (unit = path; *unit && *unit != '/'; ++unit) {
  1510. X            if (*unit == ':')
  1511. X                path = unit + 1;
  1512. X            }
  1513. X            if (*unit == '/') {
  1514. X            char *opts;
  1515. X
  1516. X            *unit = 0;
  1517. X            ++unit;
  1518. X            for (opts = unit; *opts && *opts != '/'; ++opts);
  1519. X            while (*opts) {
  1520. X                n = atoi(opts + 1);
  1521. X                switch(*opts) {
  1522. X                case '/':
  1523. X                break;
  1524. X                case 'R':
  1525. X                handle->ReadTo.tv_secs = n / 1000;
  1526. X                handle->ReadTo.tv_micro= (n % 1000) * 1000;
  1527. X                handle->Flags |= HF_RTO;
  1528. X                break;
  1529. X                case 'C':
  1530. X                handle->Flags |= HF_IGNORECD;
  1531. X                break;
  1532. X                case 'G':
  1533. X                if (n)
  1534. X                    handle->Flags |= HF_DONTLOCK;
  1535. X                break;
  1536. X                }
  1537. X                ++opts;
  1538. X            }
  1539. X            }
  1540. X
  1541. X            /* proc = (PROC *)mypkt->dp_Port->mp_SigTask; */
  1542. X
  1543. X            /*
  1544. X             * Open the device
  1545. X             */
  1546. X
  1547. X            handle->Ios.IOSer.io_Message.mn_ReplyPort = IoSink;
  1548. X            handle->Ios.io_SerFlags = SERF_XDISABLED | SERF_RAD_BOOGIE | SERF_SHARED;
  1549. X            if (OpenDevice(path, atoi(unit), (IOR *)&handle->Ios, 0L)) {
  1550. X            FreeMem(handle, sizeof(SHandle));
  1551. X            mypkt->dp_Res1 = DOS_FALSE;
  1552. X            mypkt->dp_Res2 = ERROR_OBJECT_NOT_FOUND;
  1553. X            returnpktplain(mypkt, myproc);
  1554. X            break;
  1555. X            }
  1556. X            fh->fh_Arg1 = (long)handle;
  1557. X            fh->fh_Port = (struct MsgPort *)DOS_TRUE;
  1558. X
  1559. X            handle->Iosr = handle->Ios;
  1560. X            handle->Iosw = handle->Ios;
  1561. X            handle->Iosr.IOSer.io_Command = CMD_READ;
  1562. X            handle->Iosw.IOSer.io_Command = CMD_WRITE;
  1563. X            handle->Iot = Iot;
  1564. X            NewList(&handle->RxWait);
  1565. X            NewList(&handle->TxWait);
  1566. X            NewList(&handle->CxWait);
  1567. X            AddTail(&HanList, &handle->Node);
  1568. X            returnpktplain(mypkt, myproc);
  1569. X        }
  1570. X        break;
  1571. X        case ACTION_END:
  1572. X        Remove(&handle->Node);
  1573. X        AbortPackets(handle, myproc);
  1574. X        if (handle->IotIP) {
  1575. X            AbortIO((IOR *)&handle->Iot);
  1576. X            WaitIO((IOR *)&handle->Iot);
  1577. X            handle->IotIP = 0;
  1578. X        }
  1579. X        CloseDevice((IOR *)&handle->Ios);
  1580. X        returnpktplain(mypkt, myproc);
  1581. X        if (handle->TxOut)
  1582. X            FreeMem(handle->TxOut, WRITEBUFSIZE);
  1583. X        FreeMem(handle, sizeof(SHandle));
  1584. X        break;
  1585. X        case ACTION_READ:
  1586. X        {
  1587. X            NODE *pknode = RemHead(&NodList);
  1588. X            mypkt->dp_Res1 = 0;
  1589. X            pknode->ln_Name = (char *)mypkt;
  1590. X            AddTail(&handle->RxWait, pknode);
  1591. X            AttemptRead(handle, myproc);
  1592. X        }
  1593. X        break;
  1594. X        case ACTION_WRITE:
  1595. X        {
  1596. X            NODE *pknode = RemHead(&NodList);
  1597. X            mypkt->dp_Res1 = 0;
  1598. X            pknode->ln_Name = (char *)mypkt;
  1599. X            AddTail(&handle->TxWait, pknode);
  1600. X            AttemptWrite(handle, myproc);
  1601. X        }
  1602. X        break;
  1603. X        case ACTION_WAIT_CHAR:
  1604. X        default:
  1605. X        returnpkt(mypkt, myproc, DOS_FALSE, ERROR_ACTION_NOT_KNOWN);
  1606. X        break;
  1607. X        }
  1608. X    }
  1609. X    }
  1610. X
  1611. X    /*
  1612. X     *    Can only exit if no messages pending.  There might be a window
  1613. X     *    here, but there is nothing that can be done about it.
  1614. X     */
  1615. X
  1616. X    Forbid();
  1617. X    if (taskpktrdy(myproc)) {
  1618. X    Permit();
  1619. X    goto loop;
  1620. X    }
  1621. X    mynode->dn_Task = FALSE;
  1622. X    Permit();
  1623. X
  1624. X    if (IotIP) {
  1625. X    AbortIO(&Iot.tr_node);
  1626. X    WaitIO(&Iot.tr_node);
  1627. X    }
  1628. X    CloseDevice(&Iot.tr_node);
  1629. X
  1630. X    /* we are a process "so we fall off the end of the world" */
  1631. X    /* MUST fall through */
  1632. X}
  1633. X
  1634. Xvoid
  1635. XAttemptRead(handle, myproc)
  1636. XSHandle *handle;
  1637. XPROC *myproc;
  1638. X{
  1639. X    Packet *mypkt;
  1640. X    NODE *pknode;
  1641. X
  1642. X    if (handle->Flags & HF_CDLOST) {
  1643. X    AbortPackets(handle, myproc);
  1644. X    return;
  1645. X    }
  1646. Xloop:
  1647. X    if (handle->IosrIP == 0 && (pknode = RemHead(&handle->RxWait))) {
  1648. X    long n;
  1649. X
  1650. X    AddTail(&NodList, pknode);
  1651. X
  1652. X    mypkt = (Packet *)pknode->ln_Name;
  1653. X
  1654. X    /*
  1655. X     *  special case.  If you read 0 bytes, 0 is returned if data
  1656. X     *  is pending, else -1, and NO timeout occurs.
  1657. X     */
  1658. X
  1659. X    handle->Ios.IOSer.io_Command = SDCMD_QUERY;
  1660. X    DoIO((IOR *)&handle->Ios);
  1661. X
  1662. X    if (mypkt->dp_Arg3 == 0) {
  1663. X        if (handle->Ios.IOSer.io_Actual > 0)
  1664. X        returnpkt(mypkt, myproc, DOS_FALSE, 0L);     /* 0=data rdy */
  1665. X        else
  1666. X        returnpkt(mypkt, myproc, DOS_TRUE, 0L);    /* -1=data not rdy */
  1667. X        goto loop;
  1668. X    }
  1669. X
  1670. X    if ((n = handle->Ios.IOSer.io_Actual) > 0) {
  1671. X        if (n > mypkt->dp_Arg3)
  1672. X        n = mypkt->dp_Arg3;
  1673. X        handle->Iosr.IOSer.io_Data = (APTR)mypkt->dp_Arg2;
  1674. X        handle->Iosr.IOSer.io_Length = n;
  1675. X        DoIO((IOR *)&handle->Iosr);
  1676. X        mypkt->dp_Res1 = handle->Iosr.IOSer.io_Actual;
  1677. X        returnpktplain(mypkt, myproc);
  1678. X        goto loop;
  1679. X    }
  1680. X    handle->Iosr.IOSer.io_Data = (APTR)mypkt->dp_Arg2;
  1681. X    handle->Iosr.IOSer.io_Length = 1;
  1682. X    SendIO((IOR *)&handle->Iosr);
  1683. X    handle->IosrIP = 1;
  1684. X    handle->RPacket = mypkt;
  1685. X
  1686. X    if (handle->Flags & HF_RTO) {
  1687. X        if (handle->IotIP) {
  1688. X        AbortIO(&handle->Iot.tr_node);
  1689. X        WaitIO(&handle->Iot.tr_node);
  1690. X        }
  1691. X        handle->Iot.tr_time = handle->ReadTo;
  1692. X        SendIO(&handle->Iot.tr_node);
  1693. X        handle->IotIP = 1;
  1694. X    }
  1695. X    }
  1696. X}
  1697. X
  1698. Xvoid
  1699. XAttemptWrite(handle, myproc)
  1700. XSHandle *handle;
  1701. XPROC *myproc;
  1702. X{
  1703. X    Packet *mypkt;
  1704. X    NODE *pknode;
  1705. X
  1706. X    if (handle->Flags & HF_CDLOST) {
  1707. X    AbortPackets(handle, myproc);
  1708. X    return;
  1709. X    }
  1710. X    if (handle->IoswIP == 0 && (pknode = RemHead(&handle->TxWait))) {
  1711. X    AddTail(&NodList, pknode);
  1712. X
  1713. X    mypkt = (Packet *)pknode->ln_Name;
  1714. X
  1715. X    if (handle->TxOut == NULL)
  1716. X        handle->TxOut = AllocMem(WRITEBUFSIZE, MPC);
  1717. X
  1718. X    if (mypkt->dp_Arg3 <= WRITEBUFSIZE) {   /* fully asynch */
  1719. X        movmem((char *)mypkt->dp_Arg2, handle->TxOut, mypkt->dp_Arg3);
  1720. X        handle->Iosw.IOSer.io_Data = (APTR)handle->TxOut;
  1721. X        handle->Iosw.IOSer.io_Length = mypkt->dp_Arg3;
  1722. X        SendIO(&handle->Iosw);
  1723. X        mypkt->dp_Res1 = mypkt->dp_Arg3;
  1724. X        returnpktplain(mypkt, myproc);
  1725. X        handle->WPacket = NULL;
  1726. X    } else {                /*  semi-asynch */
  1727. X        long n = mypkt->dp_Arg3 - WRITEBUFSIZE;
  1728. X        handle->Iosw.IOSer.io_Data = (APTR)mypkt->dp_Arg2;
  1729. X        handle->Iosw.IOSer.io_Length = n;
  1730. X        SendIO(&handle->Iosw);
  1731. X        movmem((char *)mypkt->dp_Arg2 + n, handle->TxOut, WRITEBUFSIZE);
  1732. X        mypkt->dp_Res1 += n;
  1733. X        handle->WPacket = mypkt;
  1734. X    }
  1735. X    handle->IoswIP = 1;
  1736. X    }
  1737. X}
  1738. X
  1739. Xvoid
  1740. XAbortPackets(handle, myproc)
  1741. XSHandle *handle;
  1742. X{
  1743. X    NODE *pknode;
  1744. X    Packet *mypkt;
  1745. X
  1746. X    if (handle->RPacket)
  1747. X    returnpktplain(handle->RPacket, myproc);
  1748. X
  1749. X    if (handle->WPacket)
  1750. X    returnpktplain(handle->WPacket, myproc);
  1751. X
  1752. X    if (handle->IosrIP) {
  1753. X    AbortIO((IOR *)&handle->Iosr);
  1754. X    WaitIO((IOR *)&handle->Iosr);
  1755. X    handle->IosrIP = 0;
  1756. X    }
  1757. X    if (handle->IoswIP) {
  1758. X    AbortIO((IOR *)&handle->Iosw);
  1759. X    WaitIO((IOR *)&handle->Iosw);
  1760. X    handle->IoswIP = 0;
  1761. X    }
  1762. X
  1763. X    handle->RPacket = NULL;
  1764. X    handle->WPacket = NULL;
  1765. X
  1766. X    while (pknode = RemHead(&handle->RxWait)) {
  1767. X    mypkt = (Packet *)pknode->ln_Name;
  1768. X    if (mypkt->dp_Arg3 == 0)
  1769. X        mypkt->dp_Res1 = 0;     /* for poll, return data rdy */
  1770. X    else
  1771. X        mypkt->dp_Res1 = -1;
  1772. X    returnpktplain(mypkt, myproc);
  1773. X    AddTail(&NodList, pknode);
  1774. X    }
  1775. X
  1776. X    while (pknode = RemHead(&handle->TxWait)) {
  1777. X    mypkt = (Packet *)pknode->ln_Name;
  1778. X    mypkt->dp_Res1 = -1;
  1779. X    returnpktplain(mypkt, myproc);
  1780. X    AddTail(&NodList, pknode);
  1781. X    }
  1782. X}
  1783. X
  1784. Xvoid
  1785. XStartTimer(secs)
  1786. X{
  1787. X    if (IotIP) {
  1788. X    AbortIO(&Iot.tr_node);
  1789. X    WaitIO(&Iot.tr_node);
  1790. X    }
  1791. X    Iot.tr_time.tv_secs = secs;
  1792. X    Iot.tr_time.tv_micro= 0;
  1793. X    SendIO(&Iot.tr_node);
  1794. X    IotIP = 1;
  1795. X}
  1796. X
  1797. X
  1798. END_OF_FILE
  1799. if test 13776 -ne `wc -c <'uucp2/src/uuser/uuser.c'`; then
  1800.     echo shar: \"'uucp2/src/uuser/uuser.c'\" unpacked with wrong size!
  1801. fi
  1802. # end of 'uucp2/src/uuser/uuser.c'
  1803. fi
  1804. echo shar: End of archive 7 \(of 12\).
  1805. cp /dev/null ark7isdone
  1806. MISSING=""
  1807. for I in 1 2 3 4 5 6 7 8 9 10 11 12 ; do
  1808.     if test ! -f ark${I}isdone ; then
  1809.     MISSING="${MISSING} ${I}"
  1810.     fi
  1811. done
  1812. if test "${MISSING}" = "" ; then
  1813.     echo You have unpacked all 12 archives.
  1814.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1815. else
  1816.     echo You still need to unpack the following archives:
  1817.     echo "        " ${MISSING}
  1818. fi
  1819. ##  End of shell archive.
  1820. exit 0
  1821. -- 
  1822. Mail submissions (sources or binaries) to <amiga@cs.odu.edu>.
  1823. Mail comments to the moderator at <amiga-request@cs.odu.edu>.
  1824. Post requests for sources, and general discussion to comp.sys.amiga.
  1825.